/*
 * Copyright (C) 2006 Roman Krashanitsa
 *
 * @author Roman Krashanitsa
 *
 * This file is part of paparazzi.
 *
 * paparazzi is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * paparazzi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with paparazzi; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA. 
 *
 */

package pprz.data_components;

import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;

public class Tile {

	static String bufPath="maps\\";
	static ImageObserver t;
	
	public static void setBufPath(String s)
	{
		bufPath=s;
	}
	
	public static void addImageObserver(ImageObserver to)
	{
		if (to!=null) t=to;
	}
	
	public static Image get(int x, int y, int z, Component c)
	{
		Image im;
		StringBuffer tileName=getSatNameByIndex(x,y,z);
		//System.out.println(tileName);
		if ((new File(getSatPath(tileName)).exists()))
		{
			im=new ImageIcon(getSatPath(tileName)).getImage();
			return im;
		}
		else 
		{
			try {
				
				BufferedImage b;
				im=Toolkit.getDefaultToolkit().getImage(getSatURL(tileName));
				MediaTracker mt = new MediaTracker(c);
			    mt.addImage(im, 0);
			    try {
			      mt.waitForID(0);
			    } catch (InterruptedException ie) {
			    }
			    if ((mt.statusID(0, false) & MediaTracker.ERRORED)==0)
			    {
			    	b = new BufferedImage(im.getWidth(null), 
			    						  im.getHeight(null), 
			    						  BufferedImage.TYPE_INT_ARGB);
			    	Graphics g = b.getGraphics();
			    	g.drawImage(im, 0, 0, null);
			    	File outputFile = new File(getSatPath(tileName));		
			    	ImageIO.write(b, "JPG", outputFile);
			    }
			    else System.out.println("Google download error: "+tileName);
				return im;
			} catch (MalformedURLException e)
			{
				System.out.println("Unable to fetch map tile..");
			} catch (IOException e)
			{}
		}
		return null;
	}
	
	private static URL getSatURL(StringBuffer s) throws MalformedURLException
	{
		URL u;
		if (s==null) u=new URL("http://www.google.com/mapfiles/transparent.gif");
		else u=new URL("http://kh1.google.com/kh?n=404&v=3&t=" + s);
		return u;
	}

	private static String getSatPath(StringBuffer s)
	{
		String str;
		if (s==null) str=bufPath+"transparent.gif";
		else str=bufPath + s + ".jpg";
		return str;
	}
	
	private static int getGridSize(int zoomLevel)
	{
		return 1<<(17-((zoomLevel>17)?17:(zoomLevel<0)?0:zoomLevel));
	}
	
	public static Point getTileLoc(double lat, double lon, int z)
	{
		int N=getGridSize(z);
		double circ=N*256; //circumference in pixels
		double radius=circ/2.0/Math.PI;
		//compute Mercator coordinates
		double x=radius*lon2x(lon); //in pixels
		double y=radius*lat2y(lat); //in pixels
		//change origin to upper-left
		x=circ/2.0+x;
		y=circ/2.0-y;
		int n=(int)(x/256);
		int m=(int)(y/256);
		return new Point(n,m);
	}
	
	public static Point getTileLoc(int x, int y, int z)
	{
//		int N=getGridSize(z);
		//double circ=N*256; //circumference in pixels
		//double radius=circ/2.0/Math.PI;
		int n=(int)(x/256);
		int m=(int)(y/256);
		return new Point(n,m);
	}
	
	public static Point LL2Pix(double lat, double lon, int z)
	{
		int N=getGridSize(z);
		double circ=N*256; //circumference in pixels
		double radius=circ/2.0/Math.PI;
		//compute Mercator coordinates
		double x=radius*lon2x(lon); //in pixels
		double y=radius*lat2y(lat); //in pixels
		//change origin to upper-left
		x=circ/2.0+x;
		y=circ/2.0-y;
		return new Point((int)x,(int)y);
	}
	
	public static Point UTM2Pix(double UTMNorthing, double UTMEasting, int z)
	{
		Point2D.Double p=GeographicConverter.UTMtoLL(23, UTMNorthing, UTMEasting, "12S");
		return LL2Pix(p.y,p.x,0);
	}	
	
	public static Point2D.Double getUTMCoord(int x, int y, int z)
	{
		int N=getGridSize(z);
		double circ=N*256; //circumference in pixels
		double radius=circ/2.0/Math.PI;
		//compute Mercator coordinates
		double lon=x2lon((double)x-circ/2.0,radius); //in degrees
		double lat=y2lat((double)circ/2.0-y,radius); //in degrees
		return new Point2D.Double(lon,lat);
	}
	
	public static StringBuffer getSatNameByIndex(int n, int m, int z)
	{
		StringBuffer str = new StringBuffer();
		str.append('t');
		//compute number of tiles
		int N=getGridSize(z);
		if (n>N || m>N || n<0 || m<0) return str;
		
		for (int i = 16; i >= z; i--) {
			
			N=N>>1;
			if (m < N) {
				if (n < N) {
					str.append('q');
				} else {
					str.append('r');
					n=n-N;
				}
			} else {
				if (n < N) {
					str.append('t');
					m=m-N;
				} else {
					str.append('s');
					m=m-N;
					n=n-N;
				}
			}
		}
		return str;		
	}
	
	static private double lon2x(double lon) {
	    return Math.toRadians(lon);
	}
	static private double lat2y(double lat) {
	    double phi = Math.toRadians(lat);
	    double y=Math.log(Math.tan(0.25*Math.PI+0.5*phi));
	    return y;
	}

	static public double x2lon(double x, double R)
	  {
	    double longRadians = x/R;
	    double longDegrees = Math.toDegrees(longRadians);
	    
	    /* The user could have panned around the world a lot of times.
	    Lat long goes from -180 to 180.  So every time a user gets 
	    to 181 we want to subtract 360 degrees.  Every time a user
	    gets to -181 we want to add 360 degrees. */
	       
	    double rotations = Math.floor((longDegrees + 180)/360);
	    double longitude = longDegrees - (rotations * 360);
	    return longitude;
	  }

	static public double y2lat(double y, double R)
	  {
	    double latitude =	(Math.PI/2) - 
	    					(2 * Math.atan(
	    					Math.exp(-1.0 * y/R)));
	    return Math.toDegrees(latitude);
	  }  

}

